# -*- coding: utf-8 -*-

import os
import time
import datetime
import uuid
import pickle
import shutil
import tempfile
from functools import wraps as func_wraps

__all__ = [
    "DiskCache",
    "cache_in_disk",
    "clear_cache",
    "set_cache"
]

class DiskCache(object):
    """缓存数据到磁盘

    实例化参数:
    ------
        cache_path: 缓存文件的路径
    """

    _NAMESPACE = uuid.UUID("c875fb30-a8a8-402d-a796-225a6b065cad")

    default_cache_path = os.path.join(tempfile.gettempdir(), ".diskcache")

    def __init__(self, cache_path=None):
        if cache_path:
            self.cache_path = os.path.abspath(cache_path)
        else:
            self.cache_path = os.getenv("CACHE_PATH") or self.default_cache_path

    def __call__(self, expire=86400, refresh=False):
        """返回一个包装后的函数
        :param expire 缓存过期时间，默认为一天，单位为秒
        :param refresh 定时刷新, 默认为 False, 刷新时间为 09:00，12:30，15:30，20:30
        """
        def decorator(func):
            @func_wraps(func)
            def wrapper(*args, **kw):
                params_uuid = uuid.uuid5(self._NAMESPACE, "-".join(map(str, (args, kw))))
                key = '{}-{}-{}.cache'.format(func.__name__, expire, str(params_uuid))
                cache_file = os.path.join(self.cache_path, key)
                if refresh:
                    if os.path.exists(cache_file):
                        # 判断缓存文件修改时间, 定时刷新
                        file_time = datetime.datetime.fromtimestamp(os.path.getmtime(cache_file))
                        refresh_time = [
                            datetime.datetime.combine(datetime.datetime.today(),
                                                      datetime.time(hour=9)),
                            datetime.datetime.combine(datetime.datetime.today(),
                                                      datetime.time(hour=12, minute=30)),
                            datetime.datetime.combine(datetime.datetime.today(),
                                                      datetime.time(hour=15, minute=30)),
                            datetime.datetime.combine(datetime.datetime.today(),
                                                      datetime.time(hour=20, minute=30))
                            ]
                        now_time = datetime.datetime.now()
                        if (file_time < refresh_time[0] < now_time or
                            file_time < refresh_time[1] < now_time or
                            file_time < refresh_time[2] < now_time or
                            file_time < refresh_time[3] < now_time):
                            os.remove(cache_file)
                if os.path.exists(cache_file):
                    # 判断缓存文件是否失效, 失效则清除
                    if time.time() - os.path.getmtime(cache_file) > int(key.split('-', 2)[1]):
                        os.remove(cache_file)
                if not os.path.exists(self.cache_path):
                    os.makedirs(self.cache_path)
                try:
                    with open(cache_file, 'rb') as f:
                        val = pickle.load(f)
                except Exception:
                    val = func(*args, **kw)
                    try:
                        with open(cache_file, 'wb') as f:
                            pickle.dump(val, f)
                    except Exception:
                        raise Exception("%s 写入失败", cache_file)
                return val
            return wrapper
        return decorator

    def _clean_expired(self):
        """清理过期的缓存"""
        if os.path.exists(self.cache_path):
            for file in os.listdir(self.cache_path):
                file_path = os.path.join(self.cache_path, file)
                if time.time() - os.path.getmtime(file_path) > int(file.split('-', 2)[1]):
                    os.remove(file_path)

    def clear_day(self, day):
        """清理超过 day 天的缓存"""
        for file in os.listdir(self.cache_path):
            file_path = os.path.join(self.cache_path, file)
            if time.time() - os.path.getmtime(file_path) > 24*3600*day:
                os.remove(file_path)

    def clear_func(self, func_name):
        """清理指定函数调用的缓存"""
        if os.path.exists(self.cache_path):
            for cache_file in os.listdir(self.cache_path):
                if cache_file.startswith(func_name + "-"):
                    os.remove(os.path.join(self.cache_path, cache_file))

    def clear_all(self):
        """清理所有缓存"""
        if os.path.exists(self.cache_path):
            shutil.rmtree(self.cache_path)
        if os.path.exists(self.default_cache_path):
            shutil.rmtree(self.default_cache_path)


cache_in_disk = DiskCache()


def clear_cache(func_name=None, day=None):
    """清除本地磁盘缓存
    params:
        func_name: 指定函数名, 例如 get_trade_days, get_industry_stocks
        day: 指定天数，正整数，清理超过 day 天的缓存
    notice:
        不加任何参数时，清理所有缓存
    """
    if func_name:
        cache_in_disk.clear_func(func_name)
    if day:
        if isinstance(day, int) and day > 0:
            cache_in_disk.clear_day(day)
        else:
            raise Exception(u'day 应该为正整数')
    if func_name is None and day is None:
        cache_in_disk.clear_all()


def set_cache(path):
    """设置本地缓存文件的路径
    args:
        path:本地缓存文件的路径
    """
    cache_in_disk.cache_path = path
